package com.jiufengxinxi.ts.common.utils.socket.server;

import java.io.IOException;
import java.net.InetAddress;
import java.nio.BufferOverflowException;
import java.nio.BufferUnderflowException;
import java.nio.channels.ClosedChannelException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Vector;

import com.jiufengxinxi.ts.common.utils.socket.ISocketRequestHandle;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xsocket.MaxReadSizeExceededException;
import org.xsocket.connection.*;


public class MultiThreadServer implements IDataHandler, IConnectHandler, IIdleTimeoutHandler, IConnectionTimeoutHandler, IDisconnectHandler {

	private Logger logger=LoggerFactory.getLogger(this.getClass());
	
	//private BaseExecutorService baseExecutorService;
	
	private ISocketRequestHandle socketRequestHandle;
	
	private List<INonBlockingConnection> connections = new Vector<INonBlockingConnection>();
	
	private String characterSet="UTF-8";
	
	private IServer srv;
	
	private int port;
	
	private String name;

	private String splitChar = null;
	
	//private Socket socket;
	
	public MultiThreadServer(int port){
		this.port = port;
		start();
	}
	
	public MultiThreadServer(int port, ISocketRequestHandle socketRequestHandle){
		this.port = port;
		this.socketRequestHandle = socketRequestHandle;
		start();
	}
	
	public void start() {
		if(srv!=null && srv.isOpen()) {
			return;
		}
		try {
			InetAddress address=InetAddress.getByName("0.0.0.0");  
	        //创建一个服务端的对象  
	        srv = new Server(address,port,this);  
	        srv.setFlushmode(IConnection.FlushMode.ASYNC);
            srv.start(); // returns after the server has been started  
            
            logger.info("服务器启动：" + srv.getLocalAddress() +":"+port);   
            Map<String, Class> maps=srv.getOptions();  
            if(maps!=null){  
                for (Entry<String, Class> entry : maps.entrySet()) {  
                	logger.info("key= "+entry.getKey()+" value ="+entry.getValue().getName());  
                }  
            }  
            logger.info("日志: " + srv.getStartUpLogMessage());  
			//baseExecutorService=new BaseExecutorService();
		} catch (IOException e) {
			logger.error(this.port+"    创建服务异常");
		}
	}
	
	public void send(String data){
		if(!srv.isOpen()){
			stop();
			start();
		}
		List<String> names = new ArrayList<String>();
		for(INonBlockingConnection connection:connections) {
			try {
				if(connection.isOpen()){
					connection.write(data);
				}else{
					names.add(connection.getAttachment().toString());
				}

			}catch (ClosedChannelException e){
				logger.error(String.format("连接断开,数据发送失败:%s to %d", data, connection.getRemoteAddress().getHostAddress()),e);
				names.add(connection.getAttachment().toString());
			}catch (Exception e) {
				if(connection!=null){
					System.out.println("client"+connection.getRemoteAddress().getHostAddress());
				}
				logger.error(String.format("数据发送失败:%s to %d", data, port),e);
			}
		}

		for(String name:names){
			stop(name);
		}
	}
	
	public void send(String data, String target){
		if(!srv.isOpen()){
			stop();
			start();
		}
		List<String> names = new ArrayList<String>();
		for(INonBlockingConnection connection:connections) {
			if(connection.getAttachment().toString().equals(target)) {
				try {
					if(connection.isOpen()){
						connection.write(data);
					}else{
						names.add(connection.getAttachment().toString());
					}
				}catch (ClosedChannelException e){
					logger.error(String.format("连接断开,数据发送失败:%s to %d", data, connection.getRemoteAddress().getHostAddress()),e);
					names.add(connection.getAttachment().toString());
				} catch (BufferOverflowException e) {
					e.printStackTrace();
				} catch (IOException e) {
					if(connection!=null){
						System.out.println("client"+connection.getRemoteAddress().getHostAddress());
					}
					logger.error(String.format("数据发送失败:%s to %d", data, port, e));
				}
			}
		}
		for(String name:names){
			stop(name);
		}
	}
	
	public void stop(){
		try {
			srv.close();
			connections.clear();
		} catch (IOException e) {
			logger.error(String.format("闭关服务异常: %d", port));
		}
		srv = null;
	}
	
	public void stop(String target){
		INonBlockingConnection conn = null;
		for(INonBlockingConnection connection:connections) {
			if(connection.getAttachment().toString().equals(target)) {
				conn = connection;
				try {
					connection.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

		if(conn!=null){
			connections.remove(conn);
		}
	}
	
	public void renameId(String oldId, String newId) {
		for(INonBlockingConnection connection:connections) {
			if(connection.getAttachment().toString().equals(oldId)) {
				connection.setAttachment(newId);
			}
		}
	}
	
	@Override
	public boolean onData(INonBlockingConnection nbc)
			throws IOException, BufferUnderflowException, ClosedChannelException, MaxReadSizeExceededException {
	   try {
		   String report = null;
		   if(splitChar!=null){
		   		Thread.sleep(50);
			   report = nbc.readStringByDelimiter(splitChar);
		   }else{
			   report=nbc.readStringByLength(nbc.available());
		   }
		   if(socketRequestHandle!=null) {
			   String res = socketRequestHandle.request(report, nbc.getAttachment().toString());
			   if(StringUtils.isNotEmpty(res)) {
				   send(res, nbc.getAttachment().toString() );
		       }
		   }
		   //System.out.println("######"+nbc.getId());
	   }catch (ClosedChannelException e) {
   			logger.error(String.format("连接断开：%d dist %s(%s)", this.port, nbc.getRemoteAddress().getHostName(), nbc.getAttachment().toString()));
		}catch (IOException e) {
			logger.error(String.format("连接断开：%d dist %s(%s)", this.port, nbc.getRemoteAddress().getHostName(), nbc.getAttachment().toString()));
		}catch (Exception e) {
		   logger.error(String.format("数据发送失败:%d", port), e);
		}
		return false;
	}
	   
	   
   /** 
    * 即当建立完连接之后可以进行的一些相关操作处理。包括修改连接属性、准备资源、等！ 
    * 连接的成功时的操作 
    */  
   @Override  
   public boolean onConnect(INonBlockingConnection nbc) throws IOException,  
           BufferUnderflowException, MaxReadSizeExceededException {  
	   nbc.setEncoding(characterSet);
	   connections.add(nbc);
       String  remoteName=nbc.getRemoteAddress().getHostAddress();  
       nbc.setAttachment(nbc.getId());
       if(socketRequestHandle!=null) {
    	   socketRequestHandle.connection(nbc.getAttachment().toString());
       }
       logger.info("服务器信息 : 客户端  " + remoteName + " 已经连接...已经断开.当前连接客户端数量:"+connections.size());
       return true;  
   }  
   /** 
    * 即如果失去连接应当如何处理？ 
    *需要实现 IDisconnectHandler  这个接口 
    * 连接断开时的操作 
    */  
   @Override  
   public boolean onDisconnect(INonBlockingConnection nbc) throws IOException { 
   	  
	  connections.remove(nbc);
      
   	  String remoteName=nbc.getRemoteAddress().getHostAddress();
   	
   	  logger.info("服务器信息:客户端 "+remoteName+" 已经断开.当前连接客户端数量:"+connections.size());

	   	if(socketRequestHandle!=null) {
	 	   socketRequestHandle.disconnection(nbc.getAttachment().toString());
	    }

      return false;  
   }  
   
   /** 
    * 请求处理超时的处理事件 
    */  
   @Override  
   public boolean onIdleTimeout(INonBlockingConnection connection) throws IOException {  
   	   logger.warn("设备处理超时："+connection.getRemoteAddress().getHostName());
       return false;  
   }  
   /** 
    * 连接超时处理事件 
    */  
   @Override  
   public boolean onConnectionTimeout(INonBlockingConnection connection) throws IOException {  
   	   logger.warn("设备连接超时："+connection.getRemoteAddress().getHostName()+" 已经断开.当前连接客户端数量:"+connections.size());
       return false;  
   } 
	
	public boolean isClosed(){
		return !(srv==null||srv.isOpen());
	}


	public int getPort() {
		return port;
	}


	public void setPort(int port) {
		this.port = port;
	}

	public String getCharacterSet() {
		return characterSet;
	}

	public void setCharacterSet(String characterSet) {
		this.characterSet = characterSet;
	}

	public ISocketRequestHandle getSocketRequestHandle() {
		return socketRequestHandle;
	}

	public void setSocketRequestHandle(ISocketRequestHandle socketRequestHandle) {
		this.socketRequestHandle = socketRequestHandle;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getSplitChar() {
		return splitChar;
	}

	public void setSplitChar(String splitChar) {
		this.splitChar = splitChar;
	}
}

